﻿using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zhao.Models.DTO;
using Zhao.Models.Entity;
using Zhao.Repository.Base;
using Zhao.Repository.UnitOfWork;

namespace Zhao.Service
{
    /// <summary>
    /// 地区缓存管理类：用于预加载、缓存、查询地区数据，避免频繁访问数据库。
    /// </summary>
    public class AreaCacheManager : BaseService<Area>
    {
        private readonly IMemoryCache _memoryCache; // 内存缓存对象
        private readonly string _allAreasCacheKey = "AllAreasCache"; // 所有地区的缓存键
        private readonly string _childrenMapCacheKey = "ChildrenMapCache"; // 父节点到子节点映射的缓存键
        private readonly IUnitOfWork _unitOfWork;

        public AreaCacheManager(IBaseRepository<Area> baseDal, IUnitOfWork unitOfWork, IMemoryCache memoryCache)
            : base(baseDal, unitOfWork)
        {
            _memoryCache = memoryCache;
            _unitOfWork = unitOfWork;
        }

        /// <summary>
        /// 初始化缓存（在系统启动时调用）
        /// 将所有地区信息和父子映射关系加载到内存中，提高后续访问效率。
        /// </summary>
        public async Task InitializeCache()
        {
            var allAreas = await GetAllAreasFromDb(); // 从数据库获取所有地区
            var childrenMap = BuildChildrenMap(allAreas); // 构建父子映射

            // 存入缓存，有效期7天（可根据实际业务调整）
            _memoryCache.Set(_allAreasCacheKey, allAreas, TimeSpan.FromDays(7));
            _memoryCache.Set(_childrenMapCacheKey, childrenMap, TimeSpan.FromDays(7));
        }

        /// <summary>
        /// 从缓存中获取所有地区数据
        /// 如果缓存不存在，则自动从数据库加载并写入缓存
        /// </summary>
        public List<Area> GetAllAreas()
        {
            return _memoryCache.GetOrCreate(_allAreasCacheKey, entry =>
            {
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(7); // 设置缓存过期时间
                return GetAllAreasFromDb().GetAwaiter().GetResult(); // 同步获取数据库数据
            });
        }

        /// <summary>
        /// 获取父节点到子节点的映射关系
        /// 如：{ 1 => [城市A, 城市B], 2 => [区A, 区B] }
        /// </summary>
        public Dictionary<int, List<Area>> GetChildrenMap()
        {
            return _memoryCache.GetOrCreate(_childrenMapCacheKey, entry =>
            {
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(7);
                var allAreas = GetAllAreas(); // 先从缓存读取所有地区
                return BuildChildrenMap(allAreas); // 构建父子关系
            });
        }

        /// <summary>
        /// 从数据库加载所有地区数据
        /// </summary>
        private async Task<List<Area>> GetAllAreasFromDb()
        {
            return await _Client.Queryable<Area>().ToListAsync(); // 使用ORM查询所有数据
        }

        /// <summary>
        /// 构建父节点ID到其子节点列表的映射字典
        /// </summary>
        private Dictionary<int, List<Area>> BuildChildrenMap(List<Area> areas)
        {
            var map = new Dictionary<int, List<Area>>();
            foreach (var area in areas)
            {
                if (!map.ContainsKey(area.ParentId))
                    map[area.ParentId] = new List<Area>();
                map[area.ParentId].Add(area); // 将area添加到其父节点的子列表中
            }
            return map;
        }

        /// <summary>
        /// 根据父ID获取地区（构建树形结构）
        /// </summary>
        public List<AreaDto> GetAreasByParentId(int parentId)
        {
            var childrenMap = GetChildrenMap();
            if (!childrenMap.TryGetValue(parentId, out var rootAreas))
                return new List<AreaDto>(); // 如果找不到父节点，返回空列表

            return BuildAreaTreeNonRecursive(rootAreas, childrenMap);
        }

        /// <summary>
        /// 使用非递归方式构建地区树（防止深度太大导致栈溢出）
        /// </summary>
        private List<AreaDto> BuildAreaTreeNonRecursive(List<Area> roots, Dictionary<int, List<Area>> map)
        {
            var result = new List<AreaDto>(); // 最终的树形结构根节点集合
            var stack = new Stack<(Area Node, List<AreaDto> ParentChildren)>();
            // 栈中保存“当前节点”和“它对应的 Children 容器”

            // Step 1: 初始化根节点并压入栈
            foreach (var root in roots.OrderByDescending(a => a.Id)) // 倒序是为了先处理小Id（先进后出）
            {
                var dto = new AreaDto { Id = root.Id, Name = root.Name }; // 创建根的 DTO
                result.Add(dto); // 加入到最终结果（根节点）
                stack.Push((root, dto.Children)); // 把根节点以及它的 Children 列表压入栈
            }

            // Step 2: 开始处理栈中的节点，模拟“递归”
            while (stack.Count > 0)
            {
                var (current, childrenList) = stack.Pop();
                // 出栈一个待处理节点：current 是 Area 实体，childrenList 是它的 Children 容器

                // 如果当前节点有子节点（即当前节点Id是别人ParentId）
                if (map.TryGetValue(current.Id, out var childAreas))
                {
                    // 遍历所有子节点（同样倒序处理）
                    foreach (var child in childAreas.OrderByDescending(a => a.Id))
                    {
                        var childDto = new AreaDto { Id = child.Id, Name = child.Name };
                        childrenList.Add(childDto); // 加入到父节点的 Children 列表中

                        // 将这个子节点继续压栈，用于后续处理它的子节点
                        stack.Push((child, childDto.Children));
                    }
                }
            }

            return result; // 返回最终构建好的树（根节点集合）
        }


        /// <summary>
        /// 获取所有城市（Level=2），并挂载子区域（如：市->区）
        /// </summary>
        public List<AreaDto> GetCity()
        {
            var allAreas = GetAllAreas(); // 所有地区
            var childrenMap = GetChildrenMap(); // 父子映射关系

            // 过滤城市级别的地区（Level=2）
            var filtered = allAreas
                .Where(a => a.Level == 2)
                .ToList();

            // 构建城市DTO并附加其下属区域
            return filtered.Select(area => new AreaDto
            {
                Id = area.Id,
                Name = area.Name,
                Children = childrenMap.GetValueOrDefault(area.Id)
                    ?.Select(x => new AreaDto
                    {
                        Id = x.Id,
                        Name = x.Name,
                        parentId = x.ParentId
                    })
                    .ToList() ?? new List<AreaDto>()
            }).ToList();
        }
    }

}
